package com.xdcplus.web.service.bd.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageInfo;
import com.xdcplus.core.utils.BeanUtils;
import com.xdcplus.exceptions.XdcWebException;
import com.xdcplus.mom.constants.NumberConstant;
import com.xdcplus.mom.enums.ResponseEnum;
import com.xdcplus.mp.service.impl.BaseServiceImpl;
import com.xdcplus.mp.utils.PageableUtils;
import com.xdcplus.mp.utils.PropertyUtils;
import com.xdcplus.pager.vo.PageVO;
import com.xdcplus.web.common.pojo.dto.bd.BdMaterialFilterDTO;
import com.xdcplus.web.common.pojo.dto.bd.BdMaterialTypeDTO;
import com.xdcplus.web.common.pojo.dto.bd.BdMaterialTypeFilterDTO;
import com.xdcplus.web.common.pojo.entity.bd.BdMaterialType;
import com.xdcplus.web.common.pojo.query.bd.BdMaterialTypeQuery;
import com.xdcplus.web.common.pojo.vo.bd.BdMaterialTypeVO;
import com.xdcplus.web.common.pojo.vo.bd.BdMaterialVO;
import com.xdcplus.web.common.utils.PageUtils;
import com.xdcplus.web.mapper.bd.BdMaterialTypeMapper;
import com.xdcplus.web.service.bd.BdMaterialService;
import com.xdcplus.web.service.bd.BdMaterialTypeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 物料类型表(BdMaterialType)表服务实现类
 *
 * @author Fish.Fei
 * @since 2023-02-01 09:04:00
 */
@Slf4j
@Service("bdMaterialTypeService")
public class BdMaterialTypeServiceImpl extends BaseServiceImpl<BdMaterialTypeMapper, BdMaterialType, BdMaterialType, BdMaterialTypeVO> implements BdMaterialTypeService {

    @Autowired
    protected BdMaterialTypeMapper bdMaterialTypeMapper;

    @Autowired
    protected BdMaterialService bdMaterialService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean saveBdMaterialType(BdMaterialTypeDTO bdMaterialTypeDTO) {
        checkCodeIsExists(bdMaterialTypeDTO.getTypeCode(),null);
        checkNameIsExists(bdMaterialTypeDTO.getTypeName(),null);
        BdMaterialType bdMaterialType = new BdMaterialType();
        BeanUtil.copyProperties(bdMaterialTypeDTO, bdMaterialType);
        bdMaterialType.setCreatedTime(new Timestamp(DateUtil.current()));
        
        return this.save(bdMaterialType);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean updateBdMaterialType(BdMaterialTypeDTO bdMaterialTypeDTO) {

        BdMaterialType bdMaterialType = this.getById(bdMaterialTypeDTO.getId());
        if (ObjectUtil.isNull(bdMaterialType)) {
            log.error("updateBdMaterialType() The BdMaterialType does not exist or has been deleted");
            throw new XdcWebException(ResponseEnum.ERROR);
        }
        checkCodeIsExists(bdMaterialTypeDTO.getTypeCode(),bdMaterialTypeDTO.getId());
        checkNameIsExists(bdMaterialTypeDTO.getTypeName(),bdMaterialTypeDTO.getId());

        BeanUtil.copyProperties(bdMaterialTypeDTO, bdMaterialType);
        bdMaterialType.setUpdatedUser(bdMaterialTypeDTO.getUpdatedUser());
        bdMaterialType.setUpdatedTime(new Timestamp(DateUtil.current()));

        return this.updateById(bdMaterialType);
    }

    @Override
    public Boolean saveOrUpdateBatch(List<BdMaterialType> bdMaterialTypeList) {

        bdMaterialTypeList.forEach(bdMaterialType -> {
            BdMaterialType bdMaterialTypeParam = new BdMaterialType();
            bdMaterialTypeParam.setId(bdMaterialType.getId());
            if (ObjectUtil.isNotNull(bdMaterialType.getId())) {
                bdMaterialType.setId(bdMaterialType.getId());
                bdMaterialType.setUpdatedTime(new Timestamp(DateUtil.current()));
                LambdaUpdateWrapper<BdMaterialType> lambdaUpdate = Wrappers.lambdaUpdate();
                lambdaUpdate.eq(BdMaterialType::getId, bdMaterialType.getId());
                update(bdMaterialType, lambdaUpdate);
            } else {
                bdMaterialType.setCreatedTime(new Timestamp(DateUtil.current()));
                save(bdMaterialType);
            }
        });
        return true;
    }

    @Override
    public Boolean saveOrUpdateBatchByDTOList(List<BdMaterialTypeDTO> bdMaterialTypeDTOList) {

        List<BdMaterialType> bdMaterialTypeList = BeanUtils.copyProperties(bdMaterialTypeDTOList, BdMaterialType.class);
        return saveOrUpdateBatch(bdMaterialTypeList);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean deleteBdMaterialTypeLogical(Long id) {

        Assert.notNull(id, ResponseEnum.THE_ID_CANNOT_BE_EMPTY.getMessage());

        BdMaterialType bdMaterialType = this.getById(id);

        if (ObjectUtil.isNull(bdMaterialType)) {
            log.error("deleteBdMaterialType() The BdMaterialType does not exist or has been deleted");
            throw new XdcWebException(ResponseEnum.ERROR);
        }
        Long codeCount = bdMaterialTypeMapper.selectCount(new QueryWrapper<BdMaterialType>().eq("PARENT_ID", id));
        Assert.isTrue(Validator.equal(codeCount,NumberConstant.ZERO.longValue()),ResponseEnum.MATERIAL_TYPE_DELETE_FAILD.getMessage());
        BdMaterialFilterDTO bdMaterialFilterDTO =new BdMaterialFilterDTO();
        bdMaterialFilterDTO.setTypeIdFk(id);
        List<BdMaterialVO> bdMaterialVOS = bdMaterialService.queryBdMaterialVOList(bdMaterialFilterDTO);
        Assert.isTrue(CollectionUtil.isEmpty(bdMaterialVOS),ResponseEnum.MATERIAL_TYPE_DELETE_FAILD_BY_MATERIAL.getMessage());

        return this.removeById(id);
    }
    
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean deleteBdMaterialTypeByIds(String ids) {
        Assert.notNull(ids, ResponseEnum.THE_ID_CANNOT_BE_EMPTY.getMessage());
		List<String> idList = Arrays.asList(ids.split(","));
		if(CollectionUtil.isEmpty(idList)){
           throw new XdcWebException(ResponseEnum.ERROR);
       }
		idList.forEach(id->{
			BdMaterialType bdMaterialType = this.getById(id);
			if (ObjectUtil.isNull(bdMaterialType)) {
				log.error("deleteBdMaterialType() The BdMaterialType does not exist or has been deleted");
				throw new XdcWebException(ResponseEnum.ERROR);
			}
		});
		
        return this.removeByIds(idList);
    }

    private List<BdMaterialType> queryBdMaterialTypeList(BdMaterialTypeFilterDTO bdMaterialTypeFilterDTO) {
        BdMaterialTypeQuery bdMaterialTypeQuery = BeanUtil.copyProperties(bdMaterialTypeFilterDTO, BdMaterialTypeQuery.class);

        return bdMaterialTypeMapper.queryBdMaterialType(bdMaterialTypeQuery);
    }

    @Override
    public List<BdMaterialTypeVO> queryBdMaterialTypeVOList(BdMaterialTypeFilterDTO bdMaterialTypeFilterDTO) {
        return this.objectConversion(queryBdMaterialTypeList(bdMaterialTypeFilterDTO));
    }

    @Override
    public PageVO<BdMaterialTypeVO> queryBdMaterialType(BdMaterialTypeFilterDTO bdMaterialTypeFilterDTO) {
        PageVO<BdMaterialTypeVO> pageVO = new PageVO<>();

        if (bdMaterialTypeFilterDTO.getCurrentPage() > NumberConstant.ZERO) {
            PageableUtils.basicPage(bdMaterialTypeFilterDTO.getCurrentPage(), bdMaterialTypeFilterDTO.getPageSize(),
                    bdMaterialTypeFilterDTO.getOrderType(), bdMaterialTypeFilterDTO.getOrderField());
        }

        List<BdMaterialType> bdMaterialTypeList = queryBdMaterialTypeList(bdMaterialTypeFilterDTO);

        PageInfo<BdMaterialType> pageInfo = new PageInfo<>(bdMaterialTypeList);
        PropertyUtils.copyProperties(pageInfo, pageVO, this.objectConversion(bdMaterialTypeList));

        return pageVO;
    }

    @Override
    public BdMaterialTypeVO queryBdMaterialTypeById(Long id) {

        Assert.notNull(id, ResponseEnum.THE_ID_CANNOT_BE_EMPTY.getMessage());

        return this.objectConversion(this.getById(id));
    }

    @Override
    public List<BdMaterialTypeVO> queryBdMaterialTypeVOByParentId(Long parentId) {
        Assert.notNull(parentId, ResponseEnum.THE_ID_CANNOT_BE_EMPTY.getMessage());

        return this.objectConversion(bdMaterialTypeMapper.queryBdMaterialTypeVOByParentId(parentId));
    }

    @Override
    public List<BdMaterialTypeVO> queryBdMaterialTypeVOTree(Long parentId) {
        Assert.notNull(parentId, ResponseEnum.THE_ID_CANNOT_BE_EMPTY.getMessage());
        List<BdMaterialTypeVO> bdMaterialTypeVOS = CollectionUtil.newArrayList();

        if (Validator.equal(NumberConstant.ZERO.longValue(), parentId)) {
            bdMaterialTypeVOS.addAll(this.objectConversion(this.list()));
        }else {
            getBdMaterialTypeVOByParentId(bdMaterialTypeVOS,parentId);
        }
        List<BdMaterialTypeVO> bdMaterialTypeVOS1 = toTree(bdMaterialTypeVOS, parentId);

        return bdMaterialTypeVOS1;
    }

    @Override
    public PageVO<BdMaterialTypeVO> queryBdMaterialTypeVOTreeWithKeyword(BdMaterialTypeFilterDTO bdMaterialTypeFilterDTO) {
        Long parentId = bdMaterialTypeFilterDTO.getParentId();
        Assert.notNull(parentId, ResponseEnum.THE_ID_CANNOT_BE_EMPTY.getMessage());
        List<BdMaterialTypeVO> bdMaterialTypeVOS = CollectionUtil.newArrayList();

        if (Validator.equal(NumberConstant.ZERO.longValue(), parentId)) {
            bdMaterialTypeVOS.addAll(this.objectConversion(queryBdMaterialTypeList(new BdMaterialTypeFilterDTO())));
        }else {
            getBdMaterialTypeVOByParentId(bdMaterialTypeVOS,parentId);
        }
        List<BdMaterialTypeVO> bdMaterialTypeVOS1 = toTree(bdMaterialTypeVOS, parentId);
        treeMatch(bdMaterialTypeVOS1, bdMaterialTypeFilterDTO.getCodeKeyword(), bdMaterialTypeFilterDTO.getNameKeyword());

        return PageUtils.getPageVO(bdMaterialTypeVOS1, bdMaterialTypeFilterDTO.getCurrentPage(),
                bdMaterialTypeFilterDTO.getPageSize() == null ? 10 : bdMaterialTypeFilterDTO.getPageSize());
    }

    @Override
    public List<BdMaterialTypeVO> getAll() {
        return baseMapper.getAll();
    }

    private void getBdMaterialTypeVOByParentId(List<BdMaterialTypeVO> bdMaterialTypeVOS, Long parentId) {
        List<BdMaterialTypeVO> bdMaterialTypeVOList = this.queryBdMaterialTypeVOByParentId(parentId);
        if (CollectionUtil.isNotEmpty(bdMaterialTypeVOList)) {
            bdMaterialTypeVOS.addAll(bdMaterialTypeVOList);
            for (BdMaterialTypeVO bdMaterialTypeVO : bdMaterialTypeVOList) {
                getBdMaterialTypeVOByParentId(bdMaterialTypeVOS, bdMaterialTypeVO.getId());
            }
        }
    }

    /**
     * 转树结构
     *
     */
    private List<BdMaterialTypeVO> toTree(List<BdMaterialTypeVO> bdMaterialTypeVOList, Long parentId) {
        if (CollectionUtil.isNotEmpty(bdMaterialTypeVOList)) {
            List<BdMaterialTypeVO> bdMaterialTypeVOS = bdMaterialTypeVOList.stream().filter(a -> Validator.equal(a.getParentId(), parentId)).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(bdMaterialTypeVOS)){
                for (BdMaterialTypeVO bdMaterialTypeVO : bdMaterialTypeVOS) {
                    List<BdMaterialTypeVO> bdMaterialTypeVOSChildren = toTree(bdMaterialTypeVOList, bdMaterialTypeVO.getId());
                    if(CollectionUtil.isNotEmpty(bdMaterialTypeVOSChildren)){
                        bdMaterialTypeVO.setChildren(bdMaterialTypeVOSChildren);
                    }
                }
            }
            return bdMaterialTypeVOS;
        }
        return null;
    }

    /**
     * 检验Code是否存在
     */
    private void checkCodeIsExists(String code,Long id){
        BdMaterialType bdMaterialType = bdMaterialTypeMapper.selectOne(new QueryWrapper<BdMaterialType>().eq("TYPE_CODE", code));

        if(bdMaterialType==null || (id!=null && bdMaterialType.getId().equals(id))){
            return;
        }
        throw new XdcWebException(ResponseEnum.MATERIAL_TYPE_CODE_ALREADY_EXISTS);
    }

    /**
     * 检验Name是否存在
     */
    private void checkNameIsExists(String name,Long id){
        BdMaterialType bdMaterialType = bdMaterialTypeMapper.selectOne(new QueryWrapper<BdMaterialType>().eq("TYPE_NAME", name));

        if(bdMaterialType==null || (id!=null && bdMaterialType.getId().equals(id))){
            return;
        }
        throw new XdcWebException(ResponseEnum.MATERIAL_TYPE_NAME_ALREADY_EXISTS);
    }

    /**
     * 递归方法
     * @param bdMaterialTypeVOList 任意层级的目录集合
     * @param codeKeyword 关键字
     * @param nameKeyword 关键字
     */
    public static void treeMatch(List<BdMaterialTypeVO> bdMaterialTypeVOList, String codeKeyword,String nameKeyword){
        Iterator<BdMaterialTypeVO> iter = bdMaterialTypeVOList.iterator();
        while (iter.hasNext()){
            // 获取当前遍历到的目录
            BdMaterialTypeVO category = iter.next();
            // 如果当前目录名称包含关键字，则什么也不做（不移除），否则就看下一级
            if(!(category.getTypeCode().contains(codeKeyword) && category.getTypeName().contains(nameKeyword))){
                // 取出下一级目录集合
                List<BdMaterialTypeVO> childrenCategoryList = category.getChildren();
                // 递归
                if(!CollectionUtil.isEmpty(childrenCategoryList)){
                    treeMatch(childrenCategoryList,codeKeyword,nameKeyword);
                }
                // 下一级目录看完了，如果下一级目录全部被移除，则移除当前目录
                if(CollectionUtil.isEmpty(category.getChildren())){
                    iter.remove();
                }
            }
        }
    }

}
